package cn.tanghom.support.shiro;

import cn.tanghom.app.system.model.Resources;
import cn.tanghom.app.system.model.Role;
import cn.tanghom.app.system.model.User;
import cn.tanghom.app.system.service.ResourcesService;
import cn.tanghom.app.system.service.RoleService;
import cn.tanghom.app.system.service.UserService;
import cn.tanghom.support.utils.CommonKey;
import cn.tanghom.support.utils.LoggerUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;

import javax.annotation.Resource;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;

/**
 * 自定义Realm,进行数据源配置
 * 
 * @author tanghom <tanghom@qq.com> 2015-11-18
 */
public class UserRealm extends AuthorizingRealm {
    @Resource
    private UserService userService;
    @Resource
    private ResourcesService resourcesService;
    @Resource
    private RoleService roleService;

    /**
     * 只有需要验证权限时才会调用, 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用.在配有缓存的情况下，只加载一次. 2015年10月27日
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addStringPermission("*");
        // 获取登录时输入的用户名
        String loginName = (String) principals.getPrimaryPrincipal();
        LoggerUtils.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>验证权限" + loginName);
        // 到数据库查是否有此对象
        User user = userService.findByLoginName(loginName);
        if (user != null) {
            if (1L == user.getId()) {// 超级管理员 始终拥有所有权限
                info.addStringPermission("*");
            }
            // 用户对应的角色
            List<Role> roles = roleService.findUserRoles(user.getId());
            HashSet hs = new LinkedHashSet();
            for (Role role : roles) {
                String roleKey = role.getRoleKey();
                hs.add(roleKey);
            }
            info.setRoles(hs);

            // 用户对应的权限
            List<Resources> resources = resourcesService.findUserResourcess(user.getId());
            for (Resources res : resources) {

                info.addStringPermission(res.getResKey());
            }
        }
        System.out.println(info.getStringPermissions());
        return info;
    }

    /**
     * 认证回调函数,登录时调用
     * 首先根据传入的用户名获取User信息；然后如果user为空，那么抛出没找到帐号异常UnknownAccountException；
     * 如果user找到但锁定了抛出锁定异常LockedAccountException；最后生成AuthenticationInfo信息，
     * 交给间接父类AuthenticatingRealm使用CredentialsMatcher进行判断密码是否匹配，
     * 如果不匹配将抛出密码错误异常IncorrectCredentialsException；
     * 另外如果密码重试此处太多将抛出超出重试次数异常ExcessiveAttemptsException；
     * 在组装SimpleAuthenticationInfo信息时， 需要传入：身份信息（用户名）、凭据（密文密码）、盐（username+salt），
     * CredentialsMatcher使用盐加密传入的明文密码和此处的密文密码进行匹配。
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        String loginName = upToken.getUsername();
        String loginPwd = String.valueOf(upToken.getPassword());
        User user = userService.findByLoginName(loginName);
        LoggerUtils.info("认证回调："+loginName);
        if (null != user) {
            if (1!=user.getStatus()) {
                throw new LockedAccountException(); // 帐号锁定
            }
            String loginPwdDb = user.getLoginPwd();
            if(!loginPwd.equals(loginPwdDb)){
                throw new IncorrectCredentialsException(); // 密码不正确
            }
            Session session = SecurityUtils.getSubject().getSession();
            session.setAttribute(CommonKey.USER_SESSION, user);
            return new SimpleAuthenticationInfo(user.getLoginName(), loginPwd, getName());
        } else {
            throw new UnknownAccountException();// 没找到帐号
        }
    }

    /**
     * 更新用户授权信息缓存.
     */
    @Override
    public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
        super.clearCachedAuthorizationInfo(principals);
    }

    /**
     * 更新用户信息缓存.
     */
    @Override
    public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
        super.clearCachedAuthenticationInfo(principals);
    }

    /**
     * 清空所有缓存
     */
    @Override
    public void clearCache(PrincipalCollection principals) {
        super.clearCache(principals);
    }

    /**
     * 清除用户授权信息缓存.
     */
    public void clearAllCachedAuthorizationInfo() {
        getAuthorizationCache().clear();
    }

    /**
     * 清除用户信息缓存.
     */
    public void clearAllCachedAuthenticationInfo() {
        getAuthenticationCache().clear();
    }

    /**
     * 清空所有认证缓存
     */
    public void clearAllCache() {
        clearAllCachedAuthenticationInfo();
        clearAllCachedAuthorizationInfo();
    }

}
